/*
@auther fenglei
*/
package xxutil

import (
	"context"
	"fmt"
	"github.com/mholt/archives"
	"io"
	"os"
	"path/filepath"
	"strings"
)

// CompressDirectory 压缩整个目录到tar.gz文件
func CompressFileTarGz(dirPath, targetFile string) error {
	// 参数验证
	if dirPath == "" || targetFile == "" {
		return fmt.Errorf("源目录和目标文件路径不能为空")
	}
	// 检查源目录是否存在
	dirInfo, err := os.Stat(dirPath)
	if os.IsNotExist(err) {
		return fmt.Errorf("源目录不存在: %s", dirPath)
	}
	if !dirInfo.IsDir() {
		return fmt.Errorf("源路径不是目录: %s", dirPath)
	}

	// 确保目标目录存在
	targetDir := filepath.Dir(targetFile)
	if err := os.MkdirAll(targetDir, 0755); err != nil {
		return fmt.Errorf("创建目标目录失败: %w", err)
	}

	// 从磁盘读取文件
	options := &archives.FromDiskOptions{
		FollowSymlinks:  false,
		ClearAttributes: false,
	}
	// 创建文件映射
	fileMap := map[string]string{
		dirPath: "", // 空字符串表示使用原始目录名
	}
	files, err := archives.FilesFromDisk(context.Background(), options, fileMap)
	if err != nil {
		return fmt.Errorf("准备文件失败: %w", err)
	}
	// 创建带超时的上下文
	//ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
	//defer cancel()
	// 创建压缩格式
	format := archives.CompressedArchive{
		Compression: archives.Gz{},
		Archival:    archives.Tar{},
	}
	// 创建输出文件
	out, err := os.Create(targetFile)
	if err != nil {
		return fmt.Errorf("创建目标文件失败: %w", err)
	}
	defer out.Close()

	// 执行压缩
	if err := format.Archive(context.Background(), out, files); err != nil {
		return fmt.Errorf("压缩目录失败: %w", err)
	}
	return nil
}

// CompressionTarGz 将指定文件压缩成 tar.gz 格式
// filePath: 要压缩的文件路径
// tarDirPath: 压缩后的目标文件路径
func CompressDirTarGz(filePath, tarDirPath string) error {
	// 参数验证
	if filePath == "" || tarDirPath == "" {
		return fmt.Errorf("源文件路径和目标路径不能为空")
	}
	// 检查源文件是否存在
	if _, err := os.Stat(filePath); os.IsNotExist(err) {
		return fmt.Errorf("源文件不存在: %s", filePath)
	}
	// 确保目标目录存在
	targetDir := filepath.Dir(tarDirPath)
	if err := os.MkdirAll(targetDir, 0755); err != nil {
		return fmt.Errorf("创建目标目录失败: %w", err)
	}
	// 创建文件映射
	fileMap := map[string]string{
		filePath: "", // 空字符串表示使用原始文件名
	}
	// 从磁盘读取文件
	options := &archives.FromDiskOptions{
		FollowSymlinks:  false,
		ClearAttributes: false,
	}
	files, err := archives.FilesFromDisk(context.Background(), options, fileMap)
	if err != nil {
		return fmt.Errorf("准备文件失败: %w", err)
	}
	// 创建带超时的上下文
	//ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
	//defer cancel()
	// 创建压缩格式
	format := archives.CompressedArchive{
		Compression: archives.Gz{},
		Archival:    archives.Tar{},
	}
	// 创建输出文件
	out, err := os.Create(tarDirPath)
	if err != nil {
		return fmt.Errorf("创建目标文件失败: %w", err)
	}
	defer out.Close()
	// 执行压缩
	if err := format.Archive(context.Background(), out, files); err != nil {
		return fmt.Errorf("压缩文件失败: %w", err)
	}
	return nil
}

// Decompose 解压缩文件到指定目录
// sourceFile: 要解压的文件路径
// targetPath: 解压的目标目录
func Decompose(sourceFile, targetPath string) error {
	// 参数验证
	if sourceFile == "" || targetPath == "" {
		return fmt.Errorf("源文件和目标目录不能为空")
	}
	// 检查源文件是否存在
	if _, err := os.Stat(sourceFile); os.IsNotExist(err) {
		return fmt.Errorf("源文件不存在: %s", sourceFile)
	}
	// 确保目标目录存在
	if err := os.MkdirAll(targetPath, 0755); err != nil {
		return fmt.Errorf("创建目标目录失败: %w", err)
	}
	// 打开源文件
	source, err := os.Open(sourceFile)
	if err != nil {
		return fmt.Errorf("打开源文件失败: %w", err)
	}
	defer source.Close()
	// 获取源文件名
	sourceName := filepath.Base(sourceFile)
	// 识别压缩格式
	//ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
	//defer cancel()

	format, reader, err := archives.Identify(context.Background(), sourceName, source)
	if err != nil {
		return fmt.Errorf("无法识别文件格式: %w", err)
	}
	// 执行解压
	extractor, ok := format.(archives.Extractor)
	if !ok {
		return fmt.Errorf("不支持的文件格式: %s", sourceName)
	}
	err = extractor.Extract(context.Background(), reader, func(ctx context.Context, f archives.FileInfo) error {
		// 跳过 Mac 系统文件
		if strings.Contains(f.NameInArchive, "__MACOSX") {
			return nil
		}
		// 构建完整的目标路径
		targetFilePath := filepath.Join(targetPath, f.NameInArchive)

		// 如果是目录，创建目录并返回
		if f.IsDir() {
			return os.MkdirAll(targetFilePath, 0755)
		}
		// 为文件创建父目录
		if err := os.MkdirAll(filepath.Dir(targetFilePath), 0755); err != nil {
			return fmt.Errorf("创建目录失败 %s: %w", filepath.Dir(targetFilePath), err)
		}
		// 打开压缩文件中的文件
		rc, err := f.Open()
		if err != nil {
			return fmt.Errorf("打开归档文件中的文件失败: %w", err)
		}
		defer rc.Close()
		// 创建目标文件
		wf, err := os.Create(targetFilePath)
		if err != nil {
			return fmt.Errorf("创建目标文件失败 %s: %w", targetFilePath, err)
		}
		defer wf.Close()
		// 复制内容
		if _, err := io.Copy(wf, rc); err != nil {
			return fmt.Errorf("写入文件失败: %w", err)
		}
		return nil
	})

	if err != nil {
		return fmt.Errorf("解压文件失败: %w", err)
	}
	return nil
}
